#include "Job.h"
#include "Effect.h"
#include "CvtColorEffect.h"
#include "GaussianBlurEffect.h"
#include "ResizeEffect.h"
#include "Utils.h"

#include <iostream>

#include "Poco/DOM/DOMParser.h"
#include "Poco/DOM/AutoPtr.h"
#include "Poco/DOM/Document.h"
#include "Poco/DOM/NodeIterator.h"
#include "Poco/DOM/NodeFilter.h"
#include "Poco/DOM/NodeList.h"
#include "Poco/DOM/NodeList.h"
#include <Poco/DOM/NamedNodeMap.h>
#include "Poco/Exception.h"

using namespace std;


using Poco::XML::DOMParser;
using Poco::XML::AutoPtr;
using Poco::XML::Document;
using Poco::XML::NodeIterator;
using Poco::XML::NodeFilter;
using Poco::XML::NodeList;
using Poco::XML::Node;
using Poco::XML::NamedNodeMap;
using Poco::Exception;

Job::Job(int resourceId, const string& xml)
{
    this->resourceId = resourceId;

    DOMParser parser;
    
    // We added trim to avoid it
    //parser.setFeature(parser.FEATURE_FILTER_WHITESPACE, true);
    AutoPtr<Document> pDoc = parser.parseString(xml);
    
    Node* jobNode = this->getNodeByName(pDoc, "JobRequest");
    
    // Parse InputRepresentation / OutputRepresentation
    Node* inputRepresentationNode = this->getNodeByName(jobNode, "InputRepresentation");
    Node* outputRepresentationNode = this->getNodeByName(jobNode, "OutputRepresentation");
    string inputRepresentationId = this->getAttributeValue(inputRepresentationNode, "id");
    string outputRepresentationId = this->getAttributeValue(outputRepresentationNode, "id");
    this->inputRepresentation = atoi(inputRepresentationId.c_str());
    this->outputRepresentation = atoi(outputRepresentationId.c_str());
    
    // Parse effectsList
    Node* effectsListNode = this->getNodeByName(jobNode, "effectsList");
    AutoPtr<NodeList> effectsChildren = effectsListNode->childNodes();
    for(unsigned long i=0;i<effectsChildren->length();i++)
    {
        Node* cEffectNode = effectsChildren->item(i);
        if(cEffectNode->nodeType() != Node::ELEMENT_NODE)
        {
            continue;
        }
        string effectName = cEffectNode->nodeName();
        cout << "Found effect: " << cEffectNode->nodeName() << endl;
        if(effectName == "cvtColor")
        {
            string code = this->getNodeValueByName(cEffectNode, "code");
            cout << "\tcode : " << code << endl;
            this->effects.push_back(shared_ptr<Effect>(new CvtColorEffect(code)));
            
        }
        else if(effectName == "resize")
        {
            string scaleFactorX = this->getNodeValueByName(cEffectNode, "scaleFactorX");
            string scaleFactorY = this->getNodeValueByName(cEffectNode, "scaleFactorY");
            string interpolation = this->getNodeValueByName(cEffectNode, "interpolation");
            cout << "\tscaleFactorX : " << scaleFactorX << endl;
            cout << "\tscaleFactorY : " << scaleFactorY << endl;
            cout << "\tinterpolation : " << interpolation << endl;
            this->effects.push_back(shared_ptr<Effect>(new ResizeEffect(scaleFactorX, scaleFactorY, interpolation)));
        
        }
        else if(effectName == "GaussianBlur")
        {
            string kSize = this->getNodeValueByName(cEffectNode, "kSize");
            string sigmaX = this->getNodeValueByName(cEffectNode, "sigmaX");
            string sigmaY = this->getNodeValueByName(cEffectNode, "sigmaY");
            string borderType = this->getNodeValueByName(cEffectNode, "borderType");
            cout << "\tkSize : " << kSize << endl;
            cout << "\tsigmaX : " << sigmaX << endl;
            cout << "\tsigmaY : " << sigmaY << endl;
            cout << "\tborderType : " << borderType << endl;
            this->effects.push_back(shared_ptr<Effect>(new GaussianBlurEffect(kSize, sigmaX, sigmaY, borderType)));
        }
        else
        {
            cout << xml << endl << endl;
            throw Exception(string("Job() unknown effect named: ") + effectName);
        }        
    }
}

Node* Job::getNodeByName(Node* pNode, const string& name)
{
    NodeIterator it(pNode, NodeFilter::SHOW_ALL);
    Node* cNode = it.nextNode();
    while (cNode)
    {    
        if(cNode->nodeName() == name)
            return cNode;
        cNode = it.nextNode();
    }
    throw Exception(string("getNodeByName failed finding node named: ") + name);
}

string Job::getNodeValueByName(Node* pNode, const string& name)
{
    Node* cNode = this->getNodeByName(pNode, name);
    string value = cNode->innerText();
    trim(value);
    return value;
}

string Job::getAttributeValue(Node* pNode, string attributeName)
{
    AutoPtr<NamedNodeMap> attributeMap = pNode->attributes();
    Node* cNode = attributeMap->getNamedItem(attributeName);
    string value = cNode->innerText();
    trim(value);
    return value;
    
}

int Job::getResourceId()
{
    return this->resourceId;
}


int Job::getInputRepresentation()
{
    return this->inputRepresentation;
}

int Job::getOutputRepresentation()
{
    return this->outputRepresentation;
}

vector< shared_ptr <Effect> > Job::getEffects(){
    return this->effects;
}
